/* 
 * File:   IceApp.cpp
 * Author: xuqian
 * 
 * Created on December 29, 2014, 6:50 PM
 */

#include "IceApp.h"
#include "common/AnotherMultiTimerThread.h"
#include "common/AnotherTimerTask.h"
#include "IceConnCheckTCallback.h"
#include "IceKeepAliveTCallback.h"
#include "Ice.h"
#include <arpa/inet.h>
#include <errno.h>

#define DEFAULT_TIMER_INTERVAL 5
#define ICE_CONTROLLING 0
#define ICE_CONTROLLED  1
#define ICE_PACKET_BUFFER_LEN 1024

// error codes
enum IceErrorCode
{
    SB_ICE_OK = 0,
    SB_ICE_CANNOT_GET_CONNCHECK_CONFIG_VALUES,
    SB_ICE_CANNOT_GET_KEEPALIVE_CONFIG_VALUES,
    SB_ICE_START_TIMER_FAILED,
    SB_ICE_CONNCHECK_PACKING_FAILED,
    SB_ICE_KEEPALIVE_PACKING_FAILED
};

IceApp::IceApp(int sockId) 
{
    timerManager = NULL;
    timerRunning = false;
    mSockId = sockId;
}

IceApp::~IceApp() 
{
    CleanUp();
}

void IceApp::setDestination(in6_addr *destAddr, unsigned short destPort) 
{     
    char dst[INET6_ADDRSTRLEN] = {0};

    if (NULL == ::inet_ntop(AF_INET6, destAddr, dst, sizeof (dst)))
    {
        LOG_ERRO("IceApp::setDestination: destAddr inet_ntop error");
    }
    
    memset(&udpAddr, 0, sizeof(struct sockaddr_in6));
    udpAddr.sin6_family = AF_INET6;
    udpAddr.sin6_port = htons(destPort);
    memcpy(&udpAddr.sin6_addr, destAddr, sizeof(in6_addr));
    
    LOG_TRAC("IceApp::setDestination: destAddr[%s], destPort[%d]", dst, destPort);
}

int IceApp::handleIceCommand(int actid, char *buf, int size)
{
    LOG_TRAC("Entering IceApp::handleSbAction");

    int ret = SB_ICE_OK;
    switch (actid) 
    {
        case mpIceConnectivityCheck:
            ret = IceConnCheckCommand((byte*)buf, size);
            break;
        case mpIceKeepAliveEnable:
            ret = IceKeepAliveCommand((byte*)buf, size);
            break;
        default:
            ret = -1;
            LOG_ERRO("Exiting IceApp::handleSbAction invalid ActId: %d.", actid);
            break;
    }
    LOG_TRAC("Exiting IceApp::handleSbAction ret[%d]", ret);
    return ret;
}

ConfigIceConnectivity * IceApp::findIceConnCheckConfig(int timerId)
{
    map<int, ConfigIceConnectivity *>::iterator iter = mConfigConnCheckMap.find(timerId);
    if (iter == mConfigConnCheckMap.end())
        return NULL;
    return iter->second;
}

ConfigIceKeepAlive * IceApp::findIceKeepAliveConfig(int timerId)
{
    map<int, ConfigIceKeepAlive *>::iterator iter = mConfigKeepAliveMap.find(timerId);
    if (iter == mConfigKeepAliveMap.end())
        return NULL;
    return iter->second;
}

bool IceApp::InitTimerManager()
{
    if (!timerManager)
    {
        timerManager = new AnotherMultiTimerThread(DEFAULT_TIMER_INTERVAL);
        if (!timerManager)
            return false;
        LOG_DBUG("IceApp::InitTimerManager() - [0x%x] create a timerManager", this);
    }
    
    if (!timerRunning)
    {
        timerManager->startTimer();
        timerRunning = true;
        LOG_DBUG("IceApp::InitTimerManager() - [0x%x] run a timerManager", this);
    }
    return true;
}

void IceApp::ReleaseTimerManager()
{
    RELEASE_POINTER(timerManager);
}

void IceApp::ReleaseConnCheckMap()
{
    LOG_DBUG("IceApp::ReleaseConnCheckMap() - Begin");
    ConfigIceConnectivity * pConfig = NULL;
    map<int, ConfigIceConnectivity *>::iterator iter;
    connCheckMapMutex.wrlock();
    for (iter = mConfigConnCheckMap.begin(); iter != mConfigConnCheckMap.end(); iter++)
    {
        pConfig = iter->second;
        LOG_DBUG("IceApp::ReleaseConnCheckMap() - connectivity check configuration 0x[%x] with timerId[%d]", pConfig, iter->first);
        mConfigConnCheckMap.erase(iter);
        RELEASE_POINTER(pConfig)
    }
    connCheckMapMutex.unlock();
    LOG_DBUG("IceApp::ReleaseConnCheckMap() - End");
}

void IceApp::ReleaseKeepAliveMap()
{
    LOG_DBUG("IceApp::ReleaseKeepAliveMap() - Begin");
    ConfigIceKeepAlive * pConfig = NULL;
    map<int, ConfigIceKeepAlive *>::iterator iter;
    keepAliveMapMutex.wrlock();
    for (iter = mConfigKeepAliveMap.begin(); iter != mConfigKeepAliveMap.end(); iter++)
    {
        pConfig = iter->second;
        LOG_DBUG("IceApp::ReleaseKeepAliveMap() - keep-alive configuration 0x[%x] with timerId[%d]", pConfig, iter->first);
        mConfigKeepAliveMap.erase(iter);
        RELEASE_POINTER(pConfig)
    }
    keepAliveMapMutex.unlock();
    LOG_DBUG("IceApp::ReleaseKeepAliveMap() - End");
}
    
void IceApp::CleanUp()
{
    ReleaseTimerManager();
    ReleaseConnCheckMap();
    ReleaseKeepAliveMap();
}

int IceApp::IceConnnectivityCheck(ConfigIceConnectivity* pConfig)
{
    int connCheck_t_id = 0;
    
    if (!InitTimerManager())
        return SB_ICE_START_TIMER_FAILED;
    
    connCheck_t_id = timerManager->addTimer(new AnotherTimerTask(pConfig->timerInterval, new IceConnCheckTCallback(this)));
    LOG_TRAC("IceApp::IceConnnectivityCheck add connectivity check timer, timer_id[%d]", connCheck_t_id);

    /* insert the timerId-configuration to map */
    connCheckMapMutex.wrlock();
    mConfigConnCheckMap.insert(map<int, ConfigIceConnectivity *>::value_type(connCheck_t_id, pConfig));
    connCheckMapMutex.unlock();
    
    /* start the timer */
    timerManager->restartTimer(connCheck_t_id);
    return SB_ICE_OK;
}

int IceApp::IceKeepAlive(ConfigIceKeepAlive* pConfig)
{
    int keepAlive_t_id = 0;
    
    if (!InitTimerManager())
        return SB_ICE_START_TIMER_FAILED;

    keepAlive_t_id = timerManager->addTimer(new AnotherTimerTask(pConfig->timerInterval, new IceKeepAliveTCallback(this)));
    LOG_TRAC("IceApp::IceConnnectivityCheck add keep-alive timer, timer_id[%d]", keepAlive_t_id);
    
    /* insert the timerId-configuration to map */
    keepAliveMapMutex.wrlock();
    mConfigKeepAliveMap.insert(map<int, ConfigIceKeepAlive *>::value_type(keepAlive_t_id, pConfig));
    keepAliveMapMutex.unlock();
    
    /* start the timer */
    timerManager->restartTimer(keepAlive_t_id);
    return SB_ICE_OK;
}

int IceApp::getIceConnCheckValues(byte* buf, int size, ConfigIceConnectivity* pConfig)
{
    int ret = SB_ICE_OK;

    pConfig->timerInterval = Util::readIntFromBuf(&buf);
    LOG_TRAC("timerInterval is [%d]ms", pConfig->timerInterval);

    pConfig->userName = Util::readStrParamFromBuf(&buf);
    LOG_TRAC("userName is [%s]", pConfig->userName.c_str());

    pConfig->roleType = Util::readIntFromBuf(&buf);
    LOG_TRAC("roleType is [%d]", pConfig->roleType);

    pConfig->roleValue = Util::readStrParamFromBuf(&buf);
    LOG_TRAC("roleValue is [%s]", pConfig->roleValue.c_str());

    pConfig->priority = Util::readStrParamFromBuf(&buf);
    LOG_TRAC("priority is [%s]", pConfig->priority.c_str());
 
    pConfig->password = Util::readStrParamFromBuf(&buf);
    LOG_TRAC("password is [%s]", pConfig->password.c_str());
    
    pConfig->useCandidate = Util::readIntFromBuf(&buf);
    LOG_TRAC("useCandidate is [%d]", pConfig->useCandidate);  
    
    return ret;
}

int IceApp::getIceKeepAliveValues(byte* buf, int size, ConfigIceKeepAlive* pConfig)
{
    int ret = SB_ICE_OK;
    int timeSec = 0;
    timeSec = Util::readIntFromBuf(&buf);
    pConfig->timerInterval = timeSec * 1000;
    LOG_TRAC("timerInterval is [%d]s", timeSec);

    return ret;
}

int IceApp::IceConnCheckCommand(byte* command_buffer, int size)
{
    int r = SB_ICE_OK;
    ConfigIceConnectivity* pConfig = NULL;
    
    pConfig = new ConfigIceConnectivity;
    if (NULL == pConfig)
    {
        LOG_TRAC("IceApp::IceConnCheckCommand(): malloc config buffer error");
        r = SB_ICE_CANNOT_GET_CONNCHECK_CONFIG_VALUES;
        return r;
    }    

    r = this->getIceConnCheckValues(command_buffer, size, pConfig);
    
    if (SB_ICE_OK == r)
    {
        /* start connectivity check timer */
        r = this->IceConnnectivityCheck(pConfig);
    }
    
    if (SB_ICE_OK != r)
    {
        delete pConfig;
        pConfig = NULL;
    }
    
    return r;    
}

int IceApp::IceKeepAliveCommand(byte* command_buffer, int size)
{
    int r = SB_ICE_OK;
    ConfigIceKeepAlive* pConfig = NULL;
    
    pConfig = new ConfigIceKeepAlive;
    if (NULL == pConfig)
    {
        LOG_TRAC("IceApp::IceKeepAliveCommand(): malloc config buffer error");
        r = SB_ICE_CANNOT_GET_KEEPALIVE_CONFIG_VALUES;
        return r;
    }
    
    r = this->getIceKeepAliveValues(command_buffer, size, pConfig);
    
    if ( SB_ICE_OK == r)
    {
        /* start keep-alive timer */
        r = this->IceKeepAlive(pConfig);
    }
        
    if (SB_ICE_OK != r)
    {
        delete pConfig;
        pConfig = NULL;
    }
    
    return r;    
}

int IceApp::assembleStunConnCheckMsg(char* outBuffer, int maxOutBufSize, ConfigIceConnectivity* pConfig)
{
    int len = 0;
    int roleValueLen = 0;
    char *iceRoleValue = NULL;    
    stStunMsg stunMsg = {0};
    
    if ( (NULL == outBuffer)
         || (NULL == pConfig) )   
        return 0;
    
    /* Create Stun request */
    stunMsg.header.type = Ice_Msg_Req;
    stunMsg.header.length = 0;
    stunMsg.header.magicCookie = ICE_MAGICCOOKIE_VALUE;
    stunMsg.attriFlag = 0;
    icePkt.generateRandNum((unsigned char *)stunMsg.header.tranID, ICE_TRANSID_LEN);
    //icePkt.hexatob((char*)"5158614f666d2b73674d5352", 24, stunMsg.header.tranID);
    
    strncpy(stunMsg.remotePwd, pConfig->password.c_str(), pConfig->password.size());
    if (!pConfig->userName.empty())
    {
        strncpy(stunMsg.Req.userName, pConfig->userName.c_str(), pConfig->userName.size());
        stunMsg.attriFlag |= ICE_ATTRIBUTE_USERNAME;
    }
    
    if (!pConfig->priority.empty())
    {
        stunMsg.Req.priority = atoi(pConfig->priority.c_str());
        stunMsg.attriFlag |= ICE_ATTRIBUTE_PRIORITY;
    }
    
    if (!pConfig->roleValue.empty())
    {
        roleValueLen = pConfig->roleValue.size();
        LOG_TRAC("assembleStunConnCheckMsg size of roleValue[%d]", roleValueLen);
        iceRoleValue = new char[roleValueLen];
        if (NULL == iceRoleValue)
        {
            LOG_ERRO("assembleStunConnCheckMsg malloc role value [%d] bytes error", roleValueLen);
            return 0;
        }
        memset(iceRoleValue, 0, roleValueLen);
   
        if (!icePkt.hexatob((char*)pConfig->roleValue.c_str(), roleValueLen, iceRoleValue))
        {
            LOG_ERRO("assembleStunConnCheckMsg hexatob error");
            delete []iceRoleValue;
            return 0;            
        }

        if (ICE_CONTROLLING == pConfig->roleType)
        {
            memcpy(stunMsg.Req.iceControlling, iceRoleValue, 8);
            stunMsg.attriFlag |= ICE_ATTRIBUTE_ICECONTROLLING;
        }
        else
        {
            memcpy(stunMsg.Req.iceControlled, iceRoleValue, 8);
            stunMsg.attriFlag |= ICE_ATTRIBUTE_ICECONTROLLED;
        }
        delete []iceRoleValue;        
    }

    if (pConfig->useCandidate)
        stunMsg.attriFlag |= ICE_ATTRIBUTE_USECANDIDATE;
    
    len = icePkt.createStunMessage(&stunMsg, (char *)outBuffer);
    if (len > maxOutBufSize)
    {
        LOG_ERRO("assembleStunConnCheckMsg length[%d] if more than max buffer len[%d]", len, maxOutBufSize);
        return 0;
    }
    
    return len;
}

int IceApp::assembleStunKeepAliveMsg(char* outBuffer, int maxOutBufSize, ConfigIceKeepAlive* pConfig)
{
    int len = 0;
    stStunMsg stunMsg = {0};
    
    if ( (NULL == outBuffer)
         || (NULL == pConfig) )
        return 0;
    
    /* Create Stun request */
    stunMsg.header.type = Ice_Msg_Ind;
    stunMsg.header.length = 0;
    stunMsg.header.magicCookie = ICE_MAGICCOOKIE_VALUE;
    stunMsg.attriFlag = ICE_ATTRIBUTE_IND_DEFAULT;
    icePkt.generateRandNum((unsigned char *)stunMsg.header.tranID, ICE_TRANSID_LEN);
        
    len = icePkt.createStunMessage(&stunMsg, (char *)outBuffer);
    if (len > maxOutBufSize)
    {
        LOG_ERRO("assembleStunConnCheckMsg length[%d] if more than max buffer len[%d]", len, maxOutBufSize);
        return 0;
    }
    
    return len;
}

void IceApp::onT_ConnCheck(int timer_id)
{
    int len = 0;
    int sendBytes = 0;
    char buffer[ICE_PACKET_BUFFER_LEN] = {0};
    ConfigIceConnectivity* pConfig = NULL;
    
    connCheckMapMutex.rdlock();
    pConfig = findIceConnCheckConfig(timer_id);
    if (NULL == pConfig)
    {
        LOG_ERRO("IceApp::onT_ConnCheck findIceConnCheckConfig error with timer_id[%d]", timer_id);
        connCheckMapMutex.unlock();
        return;
    }
    connCheckMapMutex.unlock();
    LOG_TRAC("IceApp::onT_ConnCheck findIceConnCheckConfig with timer_id[%d]", timer_id);
    
    /* Create Stun message */
    if (0 >= (len = assembleStunConnCheckMsg(buffer, ICE_PACKET_BUFFER_LEN, pConfig)))
    {
        LOG_ERRO("IceApp::onT_ConnCheck create connectivity check message failed");
        return;
    }
    
    /* send Stun message */
    if (mSockId < 0)
    {
        LOG_ERRO("IceApp::onT_ConnCheck udp socket is not ready");
        return;
    }

    LOG_TRAC("IceApp::onT_ConnCheck: send stun message len[%d]", len);
    sendBytes = ::sendto(mSockId, (void*)buffer, len, 0, (const struct sockaddr *)&udpAddr, sizeof(struct sockaddr_in6));
    if (-1 == sendBytes)
    {
        LOG_ERRO("IceApp::onT_ConnCheck: sendto error[%s]", strerror(errno));
        return;        
    }
    LOG_TRAC("IceApp::onT_ConnCheck: [%d] bytes has been sent", sendBytes);

    return;
}

void IceApp::onT_KeepAlive(int timer_id)
{
    int len = 0;
    int sendBytes = 0;
    char buffer[ICE_PACKET_BUFFER_LEN] = {0};
    ConfigIceKeepAlive* pConfig = NULL;
    
    keepAliveMapMutex.rdlock();
    pConfig = findIceKeepAliveConfig(timer_id);
    if (NULL == pConfig)
    {
        LOG_ERRO("IceApp::onT_KeepAlive findIceKeepAliveConfig error with timer_id[%d]", timer_id);
        keepAliveMapMutex.unlock();
        return;
    }
    keepAliveMapMutex.unlock();
    LOG_TRAC("IceApp::onT_KeepAlive findIceKeepAliveConfig with timer_id[%d]", timer_id);
    
    /* Create Stun message */
    if (0 >= (len = assembleStunKeepAliveMsg(buffer, ICE_PACKET_BUFFER_LEN, pConfig)))
    {
        LOG_ERRO("IceApp::onT_KeepAlive create keep-alive message failed");
        return;
    }
    
    /* send Stun message */
    if (mSockId < 0)
    {
        LOG_ERRO("IceApp::onT_KeepAlive udp socket is not ready");
        return;
    }

    LOG_TRAC("IceApp::onT_KeepAlive: send stun message len[%d]", len);
    sendBytes = ::sendto(mSockId, (void*)buffer, len, 0, (const struct sockaddr *)&udpAddr, sizeof(struct sockaddr_in6));
    if (-1 == sendBytes)
    {
        LOG_ERRO("IceApp::onT_KeepAlive: sendto error[%s]", strerror(errno));
        return;        
    }
    LOG_TRAC("IceApp::onT_KeepAlive: [%d] bytes has been sent", sendBytes);
    return;
}

int IceApp::handleIceData(char *msg, int len)
{
    stStunMsg stunMsg = {0};
    int ret = 0;
    
    if(icePkt.decodeStunMessage(msg ,len, &stunMsg, NULL))
        ret = 1;
    
    LOG_TRAC("IceApp: check it is a STUN message? %s", ret ? "true" : "false");    
    return ret;
}
